/*  */
#include "cstdlib.h"
#include "../table.h"
#include "type.h"
#include "expression.h"
#include <string.h>
#include "platform.h"
#include "variable.h"
#define MAX_FORMAT (80)
#define MAX_SCANF_ARGS (10)
FILE *Cstdlib::stdinValue = NULL;
FILE *Cstdlib::stdoutValue = NULL;
FILE *Cstdlib::stderrValue = NULL;
Cstdlib::Cstdlib() {}




void Cstdlib::BasicIOInit(Interpreter *pc) {
    pc->cStdOut = stdout;
    stdinValue = stdin;
    stdoutValue = stdout;
    stderrValue = stderr;
}

void Cstdlib::StdioOutPutc(int outCh, StdOutStream *stream) {
    if (stream->filePtr != NULL) {
        /* output to stdio stream */
        putc(outCh, stream->filePtr);
        stream->charCount++;
    } else if (stream->strOutLen < 0 || stream->strOutLen > 1) {
        /* output to a string */
        *stream->strOutPtr = outCh;
        stream->strOutPtr++;

        if (stream->strOutLen > 1)
            stream->strOutLen--;

        stream->charCount++;
    }
}

void Cstdlib::StdioOutPuts(const char *str, StdOutStream *stream) {
    if (stream->filePtr != NULL) {
        /* output to stdio stream */
        fputs(str, stream->filePtr);
    } else {
        /* output to a string */
        while (*str != '\0') {
            if (stream->strOutLen < 0 || stream->strOutLen > 1) {
                /* output to a string */
                *stream->strOutPtr = *str;
                str++;
                stream->strOutPtr++;

                if (stream->strOutLen > 1)
                    stream->strOutLen--;

                stream->charCount++;
            }
        }
    }
}

void Cstdlib::StdioFprintfWord(StdOutStream *stream, const char *format, unsigned int Value) {
    if (stream->filePtr != NULL)
        stream->charCount += fprintf(stream->filePtr, format, Value);
    else if (stream->strOutLen >= 0) {
#ifndef WIN32
        int CCount = snprintf(stream->strOutPtr, stream->strOutLen,
                              format, Value);
#else
        int CCount = _snprintf(stream->strOutPtr, stream->strOutLen,
                               format, Value);
#endif
        stream->strOutPtr += CCount;
        stream->strOutLen -= CCount;
        stream->charCount += CCount;
    } else {
        int CCount = sprintf(stream->strOutPtr, format, Value);
        stream->charCount += CCount;
        stream->strOutPtr += CCount;
    }
}

void Cstdlib::StdioFprintfLong(StdOutStream *stream, const char *format, uint64_t Value) {
    char PlatformFormat[MAX_FORMAT + 1], *FPos = PlatformFormat;

    while (*format) {
        char *UseFormat = NULL;

        switch (*format) {
        case 'd':
            UseFormat = PRId64;
            break;
        case 'i':
            UseFormat = PRIi64;
            break;
        case 'o':
            UseFormat = PRIo64;
            break;
        case 'u':
            UseFormat = PRIu64;
            break;
        case 'x':
            UseFormat = PRIx64;
            break;
        case 'X':
            UseFormat = PRIX64;
            break;
            /* Ignore the %l (long) specifier, because of course we're doing longs in this function */
        case 'l':
            break;
        default:
            *FPos++ = *format;
            break;
        }
        ++format;
        if (UseFormat) {
            strcpy(FPos, UseFormat);
            FPos += strlen(UseFormat);
        }
    }

    if (stream->filePtr != NULL)
        stream->charCount += fprintf(stream->filePtr, PlatformFormat, Value);
    else if (stream->strOutLen >= 0) {
#ifndef WIN32
        int CCount = snprintf(stream->strOutPtr, stream->strOutLen, PlatformFormat, Value);
#else
        int CCount = _snprintf(stream->strOutPtr, stream->strOutLen, PlatformFormat, Value);
#endif
        stream->strOutPtr += CCount;
        stream->strOutLen -= CCount;
        stream->charCount += CCount;
    } else {
        int CCount = sprintf(stream->strOutPtr, PlatformFormat, Value);
        stream->charCount += CCount;
        stream->strOutPtr += CCount;
    }
}

void Cstdlib::StdioFprintfFP(StdOutStream *stream, const char *format, double Value) {
    if (stream->filePtr != NULL)
        stream->charCount += fprintf(stream->filePtr, format, Value);
    else if (stream->strOutLen >= 0) {
#ifndef WIN32
        int CCount = snprintf(stream->strOutPtr, stream->strOutLen,
                              format, Value);
#else
        int CCount = _snprintf(stream->strOutPtr, stream->strOutLen,
                               format, Value);
#endif
        stream->strOutPtr += CCount;
        stream->strOutLen -= CCount;
        stream->charCount += CCount;
    } else {
        int CCount = sprintf(stream->strOutPtr, format, Value);
        stream->charCount += CCount;
        stream->strOutPtr += CCount;
    }
}

void Cstdlib::StdioFprintfPointer(StdOutStream *stream, const char *format, void *Value) {
    if (stream->filePtr != NULL)
        stream->charCount += fprintf(stream->filePtr, format, Value);
    else if (stream->strOutLen >= 0) {
#ifndef WIN32
        int CCount = snprintf(stream->strOutPtr, stream->strOutLen,
                              format, Value);
#else
        int CCount = _snprintf(stream->strOutPtr, stream->strOutLen,
                               format, Value);
#endif
        stream->strOutPtr += CCount;
        stream->strOutLen -= CCount;
        stream->charCount += CCount;
    } else {
        int CCount = sprintf(stream->strOutPtr, format, Value);
        stream->charCount += CCount;
        stream->strOutPtr += CCount;
    }
}

int Cstdlib::StdioBasePrintf(ParseState *parser, FILE *stream, char *StrOut,
                             int strOutLen, char *format, StdVararg *Args) {
    Value *thisArg = Args->param[0];
    int ArgCount = 0;
    char *FPos;
    char OneFormatBuf[MAX_FORMAT + 1];
    int OneFormatCount;
    int ShowLong = 0;
    ValueType *ShowType;
    StdOutStream SOStream;
    Interpreter *pc = parser->pc;

    if (format == NULL)
        format = "[null format]\n";

    FPos = format;
    SOStream.filePtr = stream;
    SOStream.strOutPtr = StrOut;
    SOStream.strOutLen = strOutLen;
    SOStream.charCount = 0;

    while (*FPos != '\0') {
        if (*FPos == '%') {
            /* work out what type we're printing */
            FPos++;
            ShowType = NULL;
            OneFormatBuf[0] = '%';
            OneFormatCount = 1;

            do {
                switch (*FPos) {
                case 'd':
                case 'i':
                    if (ShowLong) {
                        ShowLong = 0;
                        ShowType = &pc->longType;
                    } else {
                        ShowType = &pc->intType;
                    }
                    break;
                case 'u':
                    if (ShowLong) {
                        ShowLong = 0;
                        ShowType = &pc->unsignedLongType;
                        break;
                    }
                case 'o':
                case 'x':
                case 'X':
                    ShowType = &pc->intType;
                    break; /* integer base conversions */
                case 'l':
                    ShowLong = 1;
                    break; /* long integer */
                case 'e':
                case 'E':
                    ShowType = &pc->fPType;
                    break;      /* double, exponent form */
                case 'f':
                case 'F':
                    ShowType = &pc->fPType;
                    break;      /* double, fixed-point */
                case 'g':
                case 'G':
                    ShowType = &pc->fPType;
                    break;      /* double, flexible format */
                case 'a':
                case 'A':
                    ShowType = &pc->intType;
                    break;     /* hexadecimal, 0x- format */
                case 'c':
                    ShowType = &pc->intType;
                    break;     /* character */
                case 's':
                    ShowType = pc->charPtrType;
                    break;  /* string */
                case 'p':
                    ShowType = pc->voidPtrType;
                    break;  /* pointer */
                case 'n':
                    ShowType = &pc->voidType;
                    break;    /* number of characters written */
                case 'm':
                    ShowType = &pc->voidType;
                    break;    /* strerror(errno) */
                case '%':
                    ShowType = &pc->voidType;
                    break;    /* just a '%' character */
                case '\0':
                    ShowType = &pc->voidType;
                    break;    /* end of format string */
                }

                /* copy one character of format across to the OneFormatBuf */
                if (*FPos != 'l') {
                    OneFormatBuf[OneFormatCount] = *FPos;
                    OneFormatCount++;
                }
                /* do special actions depending on the conversion type */
                if (ShowType == &pc->voidType) {
                    switch (*FPos) {
                    case 'm':
                        StdioOutPuts(strerror(errno), &SOStream);
                        break;
                    case '%':
                        StdioOutPutc(*FPos, &SOStream);
                        break;
                    case '\0':
                        OneFormatBuf[OneFormatCount] = '\0';
                        StdioOutPutc(*FPos, &SOStream);
                        break;
                    case 'n':
                        thisArg = (Value *)((char *)thisArg +
                                            MEM_ALIGN(sizeof(Value) + Type::TypeStackSizeValue(thisArg)));
                        if (thisArg->typ->base == kTypeArray &&
                            thisArg->typ->fromType->base == kTypeInt)
                            *(int *)thisArg->val->pointer = SOStream.charCount;
                        break;
                    }
                }

                FPos++;

            } while (ShowType == NULL && OneFormatCount < MAX_FORMAT);

            if (ShowType != &pc->voidType) {
                if (ArgCount >= Args->numArgs)
                    StdioOutPuts("XXX", &SOStream);
                else {
                    /* null-terminate the buffer */
                    OneFormatBuf[OneFormatCount] = '\0';

                    /* print this argument */
                    thisArg = (Value *)((char *)thisArg +
                                        MEM_ALIGN(sizeof(Value) + Type::TypeStackSizeValue(thisArg)));

                    if (ShowType == &pc->longType) {
                        /* show a signed long */
                        if (IS_NUMERIC_COERCIBLE(thisArg))
                            StdioFprintfLong(&SOStream, OneFormatBuf, thisArg->val->longInteger);
                        else
                            StdioOutPuts("XXX", &SOStream);
                    } else if (ShowType == &pc->unsignedLongType) {
                        /* show a unsigned long */
                        if (IS_NUMERIC_COERCIBLE(thisArg))
                            StdioFprintfLong(&SOStream, OneFormatBuf, thisArg->val->unsignedLongInteger);
                        else
                            StdioOutPuts("XXX", &SOStream);
                    } else if (ShowType == &pc->intType) {
                        /* show a signed integer */
                        if (IS_NUMERIC_COERCIBLE(thisArg))
                            StdioFprintfWord(&SOStream, OneFormatBuf, (unsigned int)Expression::ExpressionCoerceUnsignedInteger(thisArg));
                        else
                            StdioOutPuts("XXX", &SOStream);
                    } else if (ShowType == &pc->fPType) {
                        /* show a floating point number */
                        if (IS_NUMERIC_COERCIBLE(thisArg))
                            StdioFprintfFP(&SOStream, OneFormatBuf,
                                           Expression::ExpressionCoerceFP(thisArg));
                        else
                            StdioOutPuts("XXX", &SOStream);
                    } else if (ShowType == pc->charPtrType) {
                        if (thisArg->typ->base == kTypePointer)
                            StdioFprintfPointer(&SOStream, OneFormatBuf,
                                                thisArg->val->pointer);
                        else if (thisArg->typ->base == kTypeArray &&
                                 thisArg->typ->fromType->base == kTypeChar)
                            StdioFprintfPointer(&SOStream, OneFormatBuf,
                                                &thisArg->val->arrayMem[0]);
                        else
                            StdioOutPuts("XXX", &SOStream);
                    } else if (ShowType == pc->voidPtrType) {
                        if (thisArg->typ->base == kTypePointer)
                            StdioFprintfPointer(&SOStream, OneFormatBuf,
                                                thisArg->val->pointer);
                        else if (thisArg->typ->base == kTypeArray)
                            StdioFprintfPointer(&SOStream, OneFormatBuf,
                                                &thisArg->val->arrayMem[0]);
                        else
                            StdioOutPuts("XXX", &SOStream);
                    }

                    ArgCount++;
                }
            }
        } else {
            /* just output a normal character */
            StdioOutPutc(*FPos, &SOStream);
            FPos++;
        }
    }

    /* null-terminate */
    if (SOStream.strOutPtr != NULL && SOStream.strOutLen > 0)
        *SOStream.strOutPtr = '\0';

    return SOStream.charCount;
}

int Cstdlib::StdioBaseScanf(ParseState *parser, FILE *stream, char *StrIn,
                            char *format, StdVararg *args) {
    Value *ThisArg = args->param[0];
    int ArgCount = 0;
    void *ScanfArg[MAX_SCANF_ARGS];

    if (args->numArgs > MAX_SCANF_ARGS)
        Platform::ProgramFail(parser, "too many arguments to scanf() - %d max",
                              MAX_SCANF_ARGS);

    for (ArgCount = 0; ArgCount < args->numArgs; ArgCount++) {
        ThisArg = (Value *)((char *)ThisArg +
                            MEM_ALIGN(sizeof(Value) + Type::TypeStackSizeValue(ThisArg)));

        if (ThisArg->typ->base == kTypePointer)
            ScanfArg[ArgCount] = ThisArg->val->pointer;
        else if (ThisArg->typ->base == kTypeArray)
            ScanfArg[ArgCount] = &ThisArg->val->arrayMem[0];
        else
            Platform::ProgramFail(parser,
                                  "non-pointer argument to scanf() - argument %d after format",
                                  ArgCount + 1);
    }

    if (stream != NULL)
        return fscanf(stream, format, ScanfArg[0], ScanfArg[1], ScanfArg[2],
                      ScanfArg[3], ScanfArg[4], ScanfArg[5], ScanfArg[6], ScanfArg[7],
                      ScanfArg[8], ScanfArg[9]);
    else
        return sscanf(StrIn, format, ScanfArg[0], ScanfArg[1], ScanfArg[2],
                      ScanfArg[3], ScanfArg[4], ScanfArg[5], ScanfArg[6], ScanfArg[7],
                      ScanfArg[8], ScanfArg[9]);
}

void Cstdlib::StdioFopen(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->pointer = fopen((const char *)param[0]->val->pointer,
                                      (const char *)param[1]->val->pointer);
}

void Cstdlib::StdioFreopen(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->pointer = freopen((const char *)param[0]->val->pointer,
                                        (const char *)param[1]->val->pointer, (FILE *)param[2]->val->pointer);
}

void Cstdlib::StdioFclose(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = fclose((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFread(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = fread(param[0]->val->pointer,
                                      param[1]->val->integer, param[2]->val->integer, (FILE *)param[3]->val->pointer);
}

void Cstdlib::StdioFwrite(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = fwrite(param[0]->val->pointer,
                                       param[1]->val->integer, param[2]->val->integer, (FILE *)param[3]->val->pointer);
}

void Cstdlib::StdioFgetc(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = fgetc((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFgets(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->pointer = fgets((char *)param[0]->val->pointer,
                                      param[1]->val->integer, (FILE *)param[2]->val->pointer);
}

void Cstdlib::StdioRemove(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = remove((const char *)param[0]->val->pointer);
}

void Cstdlib::StdioRename(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = rename((const char *)param[0]->val->pointer,
                                       (const char *)param[1]->val->pointer);
}

void Cstdlib::StdioRewind(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    rewind((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioTmpfile(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->pointer = tmpfile();
}

void Cstdlib::StdioClearerr(ParseState *parser, Value *returnValue,
                            Value **param, int numArgs) {
    clearerr((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFeof(ParseState *parser, Value *returnValue,
                        Value **param, int numArgs) {
    returnValue->val->integer = feof((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFerror(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = ferror((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFileno(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
#ifndef WIN32
    returnValue->val->integer = fileno(param[0]->val->pointer);
#else
    returnValue->val->integer = _fileno((FILE *)param[0]->val->pointer);
#endif
}

void Cstdlib::StdioFflush(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = fflush((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFgetpos(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = fgetpos((FILE *)param[0]->val->pointer,
                                        (fpos_t *)param[1]->val->pointer);
}

void Cstdlib::StdioFsetpos(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = fsetpos((FILE *)param[0]->val->pointer,
                                        (fpos_t *)param[1]->val->pointer);
}

void Cstdlib::StdioFputc(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = fputc(param[0]->val->integer,
                                      (FILE *)param[1]->val->pointer);
}

void Cstdlib::StdioFputs(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = fputs((char *)param[0]->val->pointer,
                                      (FILE *)param[1]->val->pointer);
}

void Cstdlib::StdioFtell(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = ftell((FILE *)param[0]->val->pointer);
}

void Cstdlib::StdioFseek(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    returnValue->val->integer = fseek((FILE *)param[0]->val->pointer,
                                      param[1]->val->integer, param[2]->val->integer);
}

void Cstdlib::StdioPerror(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    perror((char *)param[0]->val->pointer);
}

void Cstdlib::StdioPutc(ParseState *parser, Value *returnValue,
                        Value **param, int numArgs) {
    returnValue->val->integer = putc(param[0]->val->integer,
                                     (FILE *)param[1]->val->pointer);
}

void Cstdlib::StdioPutchar(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = putchar(param[0]->val->integer);
}

void Cstdlib::StdioSetbuf(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    setbuf((FILE *)param[0]->val->pointer, (char *)param[1]->val->pointer);
}

void Cstdlib::StdioSetvbuf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    setvbuf((FILE *)param[0]->val->pointer, (char *)param[1]->val->pointer,
            param[2]->val->integer, param[3]->val->integer);
}

void Cstdlib::StdioUngetc(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = ungetc(param[0]->val->integer,
                                       (FILE *)param[1]->val->pointer);
}

void Cstdlib::StdioPuts(ParseState *parser, Value *returnValue,
                        Value **param, int numArgs) {
    returnValue->val->integer = puts((char *)param[0]->val->pointer);
}

void Cstdlib::StdioGets(ParseState *parser, Value *returnValue,
                        Value **param, int numArgs) {
    returnValue->val->pointer = fgets((char *)param[0]->val->pointer,
                                      kGETS_MAXValue, stdin);
    if (returnValue->val->pointer != NULL) {
        char *EOLPos = strchr((char *)param[0]->val->pointer, '\n');
        if (EOLPos != NULL)
            *EOLPos = '\0';
    }
}

void Cstdlib::StdioGetchar(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = getchar();
}

void Cstdlib::StdioPrintf(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    StdVararg printfArgs;

    printfArgs.param = param;
    printfArgs.numArgs = numArgs - 1;
    returnValue->val->integer = StdioBasePrintf(parser, stdout, NULL, 0,
                                                (char *)param[0]->val->pointer, &printfArgs);
}

void Cstdlib::StdioVprintf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = StdioBasePrintf(parser, stdout, NULL, 0,
                                                (char *)param[0]->val->pointer, (StdVararg *)param[1]->val->pointer);
}

void Cstdlib::StdioFprintf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    StdVararg PrintfArgs;

    PrintfArgs.param = param + 1;
    PrintfArgs.numArgs = numArgs - 2;
    returnValue->val->integer = StdioBasePrintf(parser, (FILE *)param[0]->val->pointer,
                                                NULL, 0, (char *)param[1]->val->pointer, &PrintfArgs);
}

void Cstdlib::StdioVfprintf(ParseState *parser, Value *returnValue,
                            Value **param, int numArgs) {
    returnValue->val->integer = StdioBasePrintf(parser, (FILE *)param[0]->val->pointer,
                                                NULL, 0, (char *)param[1]->val->pointer, (StdVararg *)param[2]->val->pointer);
}

void Cstdlib::StdioSprintf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    StdVararg PrintfArgs;

    PrintfArgs.param = param + 1;
    PrintfArgs.numArgs = numArgs - 2;
    returnValue->val->integer = StdioBasePrintf(parser, NULL,
                                                (char *)param[0]->val->pointer, -1, (char *)param[1]->val->pointer, &PrintfArgs);
}

void Cstdlib::StdioSnprintf(ParseState *parser, Value *returnValue,
                            Value **param, int numArgs) {
    StdVararg PrintfArgs;

    PrintfArgs.param = param + 2;
    PrintfArgs.numArgs = numArgs - 3;
    returnValue->val->integer = StdioBasePrintf(parser, NULL,
                                                (char *)param[0]->val->pointer, param[1]->val->integer, (char *)param[2]->val->pointer,
                                                &PrintfArgs);
}

void Cstdlib::StdioScanf(ParseState *parser, Value *returnValue,
                         Value **param, int numArgs) {
    StdVararg ScanfArgs;

    ScanfArgs.param = param;
    ScanfArgs.numArgs = numArgs - 1;
    returnValue->val->integer = StdioBaseScanf(parser, stdin, NULL,
                                               (char *)param[0]->val->pointer, &ScanfArgs);
}

void Cstdlib::StdioFscanf(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    StdVararg ScanfArgs;

    ScanfArgs.param = param + 1;
    ScanfArgs.numArgs = numArgs - 2;
    returnValue->val->integer = StdioBaseScanf(parser, (FILE *)param[0]->val->pointer,
                                               NULL, (char *)param[1]->val->pointer, &ScanfArgs);
}

void Cstdlib::StdioSscanf(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    StdVararg ScanfArgs;

    ScanfArgs.param = param + 1;
    ScanfArgs.numArgs = numArgs - 2;
    returnValue->val->integer = StdioBaseScanf(parser, NULL,
                                               (char *)param[0]->val->pointer, (char *)param[1]->val->pointer, &ScanfArgs);
}

void Cstdlib::StdioVsprintf(ParseState *parser, Value *returnValue,
                            Value **param, int numArgs) {
    returnValue->val->integer = StdioBasePrintf(parser, NULL,
                                                (char *)param[0]->val->pointer, -1, (char *)param[1]->val->pointer,
                                                (StdVararg *)param[2]->val->pointer);
}

void Cstdlib::StdioVsnprintf(ParseState *parser, Value *returnValue,
                             Value **param, int numArgs) {
    returnValue->val->integer = StdioBasePrintf(parser, NULL,
                                                (char *)param[0]->val->pointer, param[1]->val->integer, (char *)param[2]->val->pointer,
                                                (StdVararg *)param[3]->val->pointer);
}

void Cstdlib::StdioVscanf(ParseState *parser, Value *returnValue,
                          Value **param, int numArgs) {
    returnValue->val->integer = StdioBaseScanf(parser, stdin, NULL,
                                               (char *)param[0]->val->pointer, (StdVararg *)param[1]->val->pointer);
}

void Cstdlib::StdioVfscanf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = StdioBaseScanf(parser, (FILE *)param[0]->val->pointer,
                                               NULL, (char *)param[1]->val->pointer, (StdVararg *)param[2]->val->pointer);
}

void Cstdlib::StdioVsscanf(ParseState *parser, Value *returnValue,
                           Value **param, int numArgs) {
    returnValue->val->integer = StdioBaseScanf(parser, NULL,
                                               (char *)param[0]->val->pointer, (char *)param[1]->val->pointer, (StdVararg *)param[2]->val->pointer);
}

const char Cstdlib::kStdioDefs[] = "\
		typedef struct __va_listStruct va_list; \
		typedef struct __FILEStruct FILE;\
		";
/* all stdio functions */
const LibraryFunction Cstdlib::StdioFunctions[] =
{
    {Cstdlib::StdioFopen, "FILE *fopen(char *, char *);"},
    {Cstdlib::StdioFreopen, "FILE *freopen(char *, char *, FILE *);"},
    {Cstdlib::StdioFclose, "int fclose(FILE *);"},
    {Cstdlib::StdioFread, "int fread(void *, int, int, FILE *);"},
    {Cstdlib::StdioFwrite, "int fwrite(void *, int, int, FILE *);"},
    {Cstdlib::StdioFgetc, "int fgetc(FILE *);"},
    {Cstdlib::StdioFgetc, "int getc(FILE *);"},
    {Cstdlib::StdioFgets, "char *fgets(char *, int, FILE *);"},
    {Cstdlib::StdioFputc, "int fputc(int, FILE *);"},
    {Cstdlib::StdioFputs, "int fputs(char *, FILE *);"},
    {Cstdlib::StdioRemove, "int remove(char *);"},
    {Cstdlib::StdioRename, "int rename(char *, char *);"},
    {Cstdlib::StdioRewind, "void rewind(FILE *);"},
    {Cstdlib::StdioTmpfile, "FILE *tmpfile();"},
    {Cstdlib::StdioClearerr,"void clearerr(FILE *);"},
    {Cstdlib::StdioFeof, "int feof(FILE *);"},
    {Cstdlib::StdioFerror, "int ferror(FILE *);"},
    {Cstdlib::StdioFileno, "int fileno(FILE *);"},
    {Cstdlib::StdioFflush, "int fflush(FILE *);"},
    {Cstdlib::StdioFgetpos, "int fgetpos(FILE *, int *);"},
    {Cstdlib::StdioFsetpos, "int fsetpos(FILE *, int *);"},
    {Cstdlib::StdioFtell, "int ftell(FILE *);"},
    {Cstdlib::StdioFseek, "int fseek(FILE *, int, int);"},
    {Cstdlib::StdioPerror, "void perror(char *);"},
    {Cstdlib::StdioPutc, "int putc(char *, FILE *);"},
    {Cstdlib::StdioPutchar, "int putchar(int);"},
    {Cstdlib::StdioPutchar, "int fputchar(int);"},
    {Cstdlib::StdioSetbuf, "void setbuf(FILE *, char *);"},
    {Cstdlib::StdioSetvbuf, "void setvbuf(FILE *, char *, int, int);"},
    {Cstdlib::StdioUngetc, "int ungetc(int, FILE *);"},
    {Cstdlib::StdioPuts, "int puts(char *);"},
    {Cstdlib::StdioGets, "char *gets(char *);"},
    {Cstdlib::StdioGetchar, "int getchar();"},
    {Cstdlib::StdioPrintf, "int printf(char *, ...);"},
    {Cstdlib::StdioFprintf, "int fprintf(FILE *, char *, ...);"},
    {Cstdlib::StdioSprintf, "int sprintf(char *, char *, ...);"},
    {Cstdlib::StdioSnprintf,"int snprintf(char *, int, char *, ...);"},
    {Cstdlib::StdioScanf, "int scanf(char *, ...);"},
    {Cstdlib::StdioFscanf, "int fscanf(FILE *, char *, ...);"},
    {Cstdlib::StdioSscanf, "int sscanf(char *, char *, ...);"},
    {Cstdlib::StdioVprintf, "int vprintf(char *, va_list);"},
    {Cstdlib::StdioVfprintf,"int vfprintf(FILE *, char *, va_list);"},
    {Cstdlib::StdioVsprintf,"int vsprintf(char *, char *, va_list);"},
    {Cstdlib::StdioVsnprintf,"int vsnprintf(char *, int, char *, va_list);"},
    {Cstdlib::StdioVscanf, "int vscanf(char *, va_list);"},
    {Cstdlib::StdioVfscanf, "int vfscanf(FILE *, char *, va_list);"},
    {Cstdlib::StdioVsscanf, "int vsscanf(char *, char *, va_list);"},
    {NULL, NULL}
};

void Cstdlib::StdioSetupFunc(Interpreter *pc) {
    ValueType *StructFileType;
    ValueType *FilePtrType;

    /* make a "struct __FILEStruct" which is the same size as a
        native FILE structure */
    StructFileType = Type::TypeCreateOpaqueStruct(pc, NULL,
                                                  MyTable::TableStrRegister(pc, "__FILEStruct"), sizeof(FILE));

    /* get a FILE * type */
    FilePtrType = Type::TypeGetMatching(pc, NULL, StructFileType, kTypePointer, 0,
                                        pc->strEmpty, true);

    /* make a "struct __va_listStruct" which is the same size as
        our StdVararg */
    Type::TypeCreateOpaqueStruct(pc, NULL, MyTable::TableStrRegister(pc, "__va_listStruct"),
                                 sizeof(FILE));

    /* define EOF equal to the system EOF */
    Variable::VariableDefinePlatformVar(pc, NULL, "EOF", &pc->intType,
                                        (AnyValue *)&EOFValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "SEEK_SET", &pc->intType,
                                        (AnyValue *)&SEEK_SETValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "SEEK_CUR", &pc->intType,
                                        (AnyValue *)&SEEK_CURValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "SEEK_END", &pc->intType,
                                        (AnyValue *)&SEEK_ENDValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "BUFSIZ", &pc->intType,
                                        (AnyValue *)&BUFSIZValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "FILENAME_MAX", &pc->intType,
                                        (AnyValue *)&FILENAME_MAXValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "_IOFBF", &pc->intType,
                                        (AnyValue *)&_IOFBFValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "_IOLBF", &pc->intType,
                                        (AnyValue *)&_IOLBFValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "_IONBF", &pc->intType,
                                        (AnyValue *)&_IONBFValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "L_tmpnam", &pc->intType,
                                        (AnyValue *)&L_tmpnamValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "GETS_MAX", &pc->intType,
                                        (AnyValue *)&kGETS_MAXValue, false);

    /* define stdin, stdout and stderr */
    Variable::VariableDefinePlatformVar(pc, NULL, "stdin", FilePtrType,
                                        (AnyValue *)&stdinValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "stdout", FilePtrType,
                                        (AnyValue *)&stdoutValue, false);
    Variable::VariableDefinePlatformVar(pc, NULL, "stderr", FilePtrType,
                                        (AnyValue *)&stderrValue, false);

    /* define NULL, true and false */
    if (!Variable::VariableDefined(pc, MyTable::TableStrRegister(pc, "NULL")))
        Variable::VariableDefinePlatformVar(pc, NULL, "NULL", &pc->intType,
                                            (AnyValue *)&Stdio_ZeroValue, false);
}

void Cstdlib::PrintCh(char outCh, FILE *stream) {
    putc(outCh, stream);
}

void Cstdlib::PrintSimpleInt(long num, FILE *stream) {
    fprintf(stream, "%ld", num);
}

void Cstdlib::PrintStr(const char *str, FILE *stream) {
    fputs(str, stream);
}

void Cstdlib::PrintFP(double num, FILE *stream) {
    fprintf(stream, "%f", num);
}


